#![feature(portable_simd)]
pub mod sha1;
pub mod sha256;
pub mod mh_sha1;
pub mod mh_sha256;

pub mod predule {
    pub const SHA1_U8_LENGTH:usize = 20;
    pub const MH_SHA1_U8_LENGTH:usize = 20;
    pub const SHA256_U8_LENGTH:usize = 32;
    pub const MH_SHA256_U8_LENGTH:usize = 32;
    pub use super::sha1::Sha1Ctx;
    pub use super::mh_sha1::IsalMhSha1Ctx;
    pub use super::sha256::Sha256Ctx;
    pub use super::mh_sha256::IsalMhSha256Ctx;
}


#[macro_use]
extern crate seq_macro;

#[cfg(test)]
mod tests {
    use super::*;
    use hex_literal::hex;
    use mh_sha1::*;
    use mh_sha256::*;
    use sha1::Sha1Ctx;
    use sha256::Sha256Ctx;

    fn test_sha1(data:&[u8],expected_sha1:[u8;20]) {
        // create a Sha1 object
        let mut hasher = Sha1Ctx::new();

        // process input message
        hasher.update(data);

        // acquire hash digest in the form of GenericArray,
        // which in this case is equivalent to [u8; 20]
        let mut result = [0u8;20];
        hasher.finalize(&mut result);
        assert_eq!(result[..], expected_sha1);
    }

    fn test_sha256(data:&[u8],expected_sha1:[u8;32]) {
        // create a Sha1 object
        let mut hasher = Sha256Ctx::new();

        // process input message
        hasher.update(data);

        // acquire hash digest in the form of GenericArray,
        // which in this case is equivalent to [u8; 20]
        let mut result = [0u8;32];
        hasher.finalize(&mut result);
        assert_eq!(result[..], expected_sha1);
    }

    fn test_mh_sha1<T:MhSha1BlockFunction+std::fmt::Debug>(data:&[u8],sha1:&mut [u8;20], expected_sha1:Option<&[u8;20]>, name:&str) {
        let now = std::time::Instant::now();
        // create a Sha1 object
        let mut hasher1 = IsalMhSha1Ctx::<T>::new();

        hasher1.update(data);
        
        hasher1.finalize(sha1);

        let time= now.elapsed().as_micros();
        println!("{}_sha1_time:{}", name, time);
        if let Some(expected_sha1) = expected_sha1 {
            assert_eq!(sha1, expected_sha1);
        }
    }

    fn test_mh_sha256<T:MhSha256BlockFunction+std::fmt::Debug>(data:&[u8],sha256:&mut [u8;32], expected_sha256:Option<&[u8;32]>, name:&str) {
        let now = std::time::Instant::now();
        // create a Sha256 object
        let mut hasher1 = IsalMhSha256Ctx::<T>::new();

        hasher1.update(data);
        
        hasher1.finalize(sha256);

        let time= now.elapsed().as_micros();
        println!("{}_sha256_time:{}", name, time);
        if let Some(expected_sha256) = expected_sha256 {
            assert_eq!(sha256, expected_sha256);
        }
    }

    #[cfg(all(any(target_arch="x86", target_arch="x86_64"),feature="simd"))]
    fn test_all_mh_sha1(data:&[u8]) {
        use mh_sha1::x86::*;
        let mut correct_result = [0u8;20];
        test_mh_sha1::<MhSha1Base>(data, &mut correct_result, None, "rs_base");
        
        let mut result = [0u8;20];
        // test_c_avx512_mh_sha1(data, &mut result, Some(&correct_result), "c_avx512");
        test_mh_sha1::<MhSha1Simdx4>(data, &mut result, Some(&correct_result), "rs_simdx4");
        test_mh_sha1::<MhSha1Simdx16>(data, &mut result, Some(&correct_result), "rs_simdx16");
        test_mh_sha1::<MhSha1Sse>(data, &mut result, Some(&correct_result), "rs_sse");
        test_mh_sha1::<MhSha1Avx>(data, &mut result, Some(&correct_result), "rs_avx");
        test_mh_sha1::<MhSha1Avx2>(data, &mut result, Some(&correct_result), "rs_avx2");
        test_mh_sha1::<MhSha1Avx512>(data, &mut result, Some(&correct_result), "rs_avx512");
    }

    #[cfg(all(any(target_arch="arm", target_arch="aarch64"),feature="simd"))]
    fn test_all_mh_sha1(data:&[u8]) {
        use mh_sha1::aarch64::*;
        let mut correct_result = [0u8;20];
        test_mh_sha1::<MhSha1Base>(data, &mut correct_result, None, "rs_base");
        
        let mut result = [0u8;20];
        test_mh_sha1::<MhSha1Simdx4>(data, &mut result, Some(&correct_result), "rs_simdx4");
        test_mh_sha1::<MhSha1Simdx16>(data, &mut result, Some(&correct_result), "rs_simdx16");
        test_mh_sha1::<MhSha1Ce>(data, &mut result, Some(&correct_result), "rs_ce");
        test_mh_sha1::<MhSha1Asimd>(data, &mut result, Some(&correct_result), "rs_asimd");
        test_mh_sha1::<MhSha1Sve>(data, &mut result, Some(&correct_result), "rs_sve");
        test_mh_sha1::<MhSha1Sve2>(data, &mut result, Some(&correct_result), "rs_sve2");
    }

    #[cfg(not(feature="simd"))]
    fn test_all_mh_sha1(data:&[u8]) {
        let mut correct_result = [0u8;20];
        test_mh_sha1::<MhSha1Base>(data, &mut correct_result, None, "rs_base");
        
        let mut result = [0u8;20];
        // test_avx512_mh_sha1(data, &mut result, Some(&correct_result), "c_avx512");
        test_mh_sha1::<MhSha1Simdx4>(data, &mut result, Some(&correct_result), "rs_simdx4");
        test_mh_sha1::<MhSha1Simdx16>(data, &mut result, Some(&correct_result), "rs_simdx16");
    }

    #[cfg(all(any(target_arch="x86", target_arch="x86_64"),feature="simd"))]
    fn test_all_mh_sha256(data:&[u8]) {
        use mh_sha256::x86::*;
        let mut correct_result = [0u8;32];
        test_mh_sha256::<MhSha256Base>(data, &mut correct_result, None, "rs_base");
        //test_c_avx512_mh_sha256(data, &mut correct_result, None, "c_avx512");

        let mut result = [0u8;32];
        test_mh_sha256::<MhSha256Sse>(data, &mut result, Some(&correct_result), "rs_sse");
        test_mh_sha256::<MhSha256Avx>(data, &mut result, Some(&correct_result), "rs_avx");
        test_mh_sha256::<MhSha256Avx2>(data, &mut result, Some(&correct_result), "rs_avx2");
        test_mh_sha256::<MhSha256Avx512>(data, &mut result, Some(&correct_result), "rs_avx512");
    }

    #[cfg(all(any(target_arch="arm", target_arch="aarch64"),feature="simd"))]
    fn test_all_mh_sha256(data:&[u8]) {
        use mh_sha256::aarch64::*;
        let mut correct_result = [0u8;20];
        test_mh_sha256::<MhSha256Base>(data, &mut correct_result, None, "rs_base");
        
        let mut result = [0u8;20];
        test_mh_sha256::<MhSha256Ce>(data, &mut result, Some(&correct_result), "rs_ce");
    }

    #[cfg(not(feature="simd"))]
    fn test_all_mh_sha256(data:&[u8]) {
        let mut correct_result = [0u8;20];
        test_mh_sha256::<MhSha256Base>(data, &mut correct_result, None, "rs_base");
        
        // let mut result = [0u8;20];
        // test_avx512_mh_sha256(data, &mut result, Some(&correct_result), "c_avx512");
    }
    
    // fn test_c_avx512_mh_sha1(data:&[u8],sha1:&mut [u8;20], expected_sha1:Option<&[u8;20]>, name:&str) {
    //     let now = std::time::Instant::now();
    //     // create a Sha1 object
    //     let mut hasher1 = superfeature_avx::MhSha1Ctx::new();

    //     hasher1.update_avx512(data);
        
    //     hasher1.finalize_avx512(sha1);

    //     let time = now.elapsed().as_micros();
    //     println!("{}_sha1_time:{}", name, time);
    //     if let Some(expected_sha1) = expected_sha1 {
    //         assert_eq!(sha1, expected_sha1);
    //     }
    // }

    // fn test_c_avx512_mh_sha256(data:&[u8],sha256:&mut [u8;32], expected_sha256:Option<&[u8;32]>, name:&str) {
    //     let now = std::time::Instant::now();
    //     // create a Sha256 object
    //     let mut hasher1 = superfeature_avx::MhSha256Ctx::new();

    //     hasher1.update_avx512(data);
        
    //     hasher1.finalize_avx512(sha256);

    //     let time = now.elapsed().as_micros();
    //     println!("{}_sha256_time:{}", name, time);
    //     if let Some(expected_sha256) = expected_sha256 {
    //         assert_eq!(sha256, expected_sha256);
    //     }
    // }

    //mh_sha1[b"hello"] = hex!(""7b857c8510c642a6b753e553791774033657ce65"")
    #[test]
    fn it_works() {
        // test sha1
        test_sha1(b"", hex!("da39a3ee5e6b4b0d3255bfef95601890afd80709"));
        test_sha1(b"abc", hex!("a9993e364706816aba3e25717850c26c9cd0d89d"));
        test_sha1(b"hello world", hex!("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"));

        // test sha256
        test_sha256(b"", hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
        test_sha256(b"The quick brown fox jumps over the lazy dog", hex!("d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"));
        test_sha256(b"The quick brown fox jumps over the lazy dog.", hex!("ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c"));

        // test mh_sha1
        let src = vec![0;1000000];
        let data = &src;
        test_all_mh_sha1(data);
        test_all_mh_sha256(data);
    }
}
